#include "encoding.h"
#include "common/stringutils.h"
#include "common/symbol.h"
#include "common/versions.h"
#include "common/dataencodings.h"
#include "common/alphanumerictable.h"

#include <QString>

#include <bitset>
#include <utility>

#include <QDebug>

namespace qrcode {

Encoding::Encoding()
{
    dataEncodingCode = UTF8;
}

void Encoding::DoEncoding(std::string &mode, int version, std::string &msg, char &level)
{
    //ECI mode indicator and ECI Assignment number
    if(mode == MODEL_INDICATOR_ECI){
        DataEncodings *qencoding = new DataEncodings();
        std::vector<char> assignment = qencoding->GetECIIndicator(dataEncodingCode);
        for(int i = 0; i < assignment.size(); i++){
            //qDebug() << assignment.at(i);
            binaryData.push_back(assignment.at(i));
        }
        //8位字节指示符
        binaryData.push_back('0');
        binaryData.push_back('1');
        binaryData.push_back('0');
        binaryData.push_back('0');
        qencoding->ConvertCode(msg, dataEncodingCode);
    } else {

    //Other Mode Indicator
    binaryData.push_back(mode.at(0));
    binaryData.push_back(mode.at(1));
    binaryData.push_back(mode.at(2));
    binaryData.push_back(mode.at(3));

    //如果是汉字模式，则转换为gb2312编码,并且添加中国汉字子集指示符（4位，0001）
    if(mode == MODEL_INDICATOR_HZ){

        //StringUtils::Utf8ToShift_JIS(msg);

        qDebug() << "汉字子集指示符: " << "0001";
        StringUtils::Utf8ToGB2312(msg);
        binaryData.push_back('0');
        binaryData.push_back('0');
        binaryData.push_back('0');
        binaryData.push_back('1');
    }
    }


    //2.Character Count Indicator
    Versions *qVer = new Versions();
    std::string charCountIndi = qVer->GetCharacterCountIndicator(mode, version, msg.length());
    for(int i = 0; i < charCountIndi.length(); ++i){
        binaryData.push_back(charCountIndi.at(i));
    }
    StringUtils::PRINT("字符计数指示符：" + QString::fromStdString(charCountIndi));

    //3.Encoded Data
    std::vector<char> dataBits;
    if(mode == MODEL_INDICATOR_NUM) {
        dataBits = NumericModeEncoding(msg);
    } else if(mode == MODEL_INDICATOR_AN) {
        dataBits = AlphanumericModeEncoding(msg);
    } else if(mode == MODEL_INDICATOR_BYTE) {
        dataBits = ByteModeEncoding(msg);
    } else if(mode == MODEL_INDICATOR_ECI) {
        dataBits = ECIModeEncoding(msg);
    } else if(mode == MODEL_INDICATOR_HZ) {
        dataBits = HanziModeEncoding(msg);
    } else {

    }

    for(int i = 0; i < dataBits.size(); ++i){
        binaryData.push_back(dataBits.at(i));
    }

    //4.添加终止符
    int maxBits = qVer->GetMaxDataBits(version, level);
    for(int i = 0; i < TERM.length(); ++i){
        if(binaryData.size() < maxBits){
            binaryData.push_back(TERM.at(i));
        } else {
            qDebug() << "encoding.cpp 67: 数据长度超出最大容量";
        }
    }

    //5.将数据位流划分为一个个码字，每个码字是8位bit，最后一个码字不足8位时补0
    int maxCodeWords = maxBits / 8;
    codeWords = BreakUpIntoCodeWords(binaryData, maxCodeWords);

}



std::vector<char> Encoding::ECIModeEncoding(std::__cxx11::string &msg)
{
    std::vector<char> encodingBits;
    //1.转换为UTF-8编码
    QString qStr = QString::fromStdString(msg);
    QByteArray byteArray = qStr.toUtf8();  //QString转换为char*
    const char *s = byteArray.data();
    //2.将每个字符转换为对应的8位二进制
    while(*s){
        std::bitset<8> bits = int(*s);
        std::string tmp_str = bits.to_string();
        for(int i = 0; i < tmp_str.length(); ++i){
            encodingBits.push_back(tmp_str.at(i));
        }
        s++;
    }

    return encodingBits;
}

std::vector<char> Encoding::NumericModeEncoding(std::string &msg)
{
    std::vector<char> encodingBits;
    std::vector<QString> dataGroup;
    //1.转换为每3位一组
    for(int i = 0; i < msg.length(); ++i){
        QString tmp;
        for(int j = 0; j < 3; ++j){
            if(i >= msg.length()){
                break;
            }
            tmp.append(msg.at(i));
            i++;
        }
        i--;
        dataGroup.push_back(tmp);
    }

    //2.将每组数据转换为二进制
    for(int i = 0; i < dataGroup.size(); ++i){
        QString tmp = dataGroup.at(i);

        std::string tmp_bit;
        bool ok;
        int tmp_i;
        switch (tmp.length()) {
        case 3:
        {
            tmp_i = tmp.toInt(&ok, 10);
            std::bitset<10> bits3 = tmp_i;
            tmp_bit = bits3.to_string();

            break;
        }
        case 2:
        {
            tmp_i = tmp.toInt(&ok, 10);
            std::bitset<7> bits2 = tmp_i;
            tmp_bit = bits2.to_string();
            break;
        }
        case 1:
        {
            tmp_i = tmp.toInt(&ok, 10);
            std::bitset<4> bits1 = tmp_i;
            tmp_bit = bits1.to_string();
            break;
        }
        default:
        {
            qDebug() << "encoding.cpp 66: 数据分组错误。";
            break;
        }
        }

        for(int j = 0; j < tmp_bit.length(); ++j){
            encodingBits.push_back(tmp_bit.at(j));
        }
    }

    return encodingBits;
}

std::vector<char> Encoding::AlphanumericModeEncoding(std::string &msg)
{
    std::vector<char> encodingBits;
    std::vector<std::string> dataGroup;
    //1.转换为每2个一组
    for(int i = 0; i < msg.length(); ++i){
        std::string tmp;
        for(int j = 0; j < 2; ++j){
            if(i >= msg.length()){
                break;
            }
            tmp.push_back(msg.at(i));
            i++;
        }
        i--;
        dataGroup.push_back(tmp);
    }

    //2.为每组值创建二进制值
    AlphanumericTable *qANTable = new AlphanumericTable();
    for(int i = 0; i < dataGroup.size(); ++i){
        std::string str = dataGroup.at(i);

        int tmp_i;
        std::string tmp_bits;
        switch (str.length()) {
        case 2:
        {
            int first_i = qANTable->GetValueByChar(str.at(0));
            int second_i = qANTable->GetValueByChar(str.at(1));
            tmp_i = first_i * 45 + second_i;
            std::bitset<11> bits2 = tmp_i;
            tmp_bits = bits2.to_string();
            break;
        }
        case 1:
        {
            tmp_i = qANTable->GetValueByChar(str.at(0));
            std::bitset<6> bits1 = tmp_i;
            tmp_bits = bits1.to_string();
            break;
        }
        default:
        {
            qDebug() << "encoding.cpp 145: 分组错误。";
            break;
        }
        }

        for(int j = 0; j < tmp_bits.length(); ++j){
            encodingBits.push_back(tmp_bits.at(j));
        }
    }
    return encodingBits;
}

std::vector<char> Encoding::ByteModeEncoding(std::__cxx11::string &msg)
{
    std::vector<char> encodingBits;
    //1.转换为ISO 8859-1(Latin1)编码
    QString qStr = QString::fromStdString(msg);
    QByteArray byteArray = qStr.toLatin1();  //QString转换为char*
    const char *s = byteArray.data();
    //2.将每个字符转换为对应的8位二进制
    while(*s){
        std::bitset<8> bits = int(*s);
        std::string tmp_str = bits.to_string();
        for(int i = 0; i < tmp_str.length(); ++i){
            encodingBits.push_back(tmp_str.at(i));
        }
        s++;
    }

    return encodingBits;
}

std::vector<char> Encoding::HanziModeEncoding(std::__cxx11::string &msg)
{
    std::vector<char> encodingBits;
    //1.每两个字节表示一个字符，进行分组
    typedef std::pair<int, int> word;
    std::vector<word> dataGroup;
    word tmp_w;
    unsigned char tmp_ch1;
    unsigned char tmp_ch2;
    for(int i = 0; i < msg.length(); i += 2) {
        tmp_ch1 = msg.at(i);
        tmp_ch2 = msg.at(i+1);
        tmp_w = std::make_pair(int(tmp_ch1), int(tmp_ch2));
        dataGroup.push_back(tmp_w);
    }

    //2.根据GB/T 18284-2000提供的方法进行计算,
    /**
     * a. 对于第一个字节值在0xA1(161)～0xAA(170)范围，第二字节值在0xA1(161)~0xFE(254)范围的字符：
     *    1）第一个字节减去0xA1(161);
     *    2）将 1）的结果乘以0x60(96);
     *    3）第二个字节减去0xA1(161);
     *    4）将2）的结果加上3）的结果;
     *
     * b. 对于第一个字节值在0xB0(176)～0xFA(250)范围，第二字节值在0xA1(161)~0xFE(254)范围的字符：
     *    1）第一个字节减去0xA6(166);
     *    2）将 1）的结果乘以0x60(96);
     *    3）第二个字节减去0xA1(161);
     *    4）将2）的结果加上3）的结果;
     */
    word w;
    std::string tmp_str;
    int val;
    for(int i = 0; i < dataGroup.size(); ++i){
        w = dataGroup.at(i);
        if(w.first >= 161 && w.first <= 170){
            val = (w.first - 161) * 96 + (w.second - 161);
        } else if(w.first >= 176 && w.first <= 250) {
            val = (w.first - 166) * 96 + (w.second - 161);
        } else {
            qDebug() << "encoding.cpp 223: 数据不在编码范围内";
        }

        //3.将结果转换为13位二进制
        std::bitset<13> bits = val;
        tmp_str = bits.to_string();
        for(int j = 0; j < tmp_str.length(); ++j) {
            encodingBits.push_back(tmp_str.at(j));
        }
    }


//    word w;
//    std::string tmp_str;
//    int val;
//    for(int i = 0; i < dataGroup.size(); ++i){
//        w = dataGroup.at(i);
//        if(w.first >= 129 && w.first <= 159 && w.second >= 64 && w.second <= 252){
//            val = (w.first - 129) * 192 + (w.second - 64);
//        } else if(w.first >= 224 && w.first <= 235 && w.second >= 64 && w.second <= 252) {
//            val = (w.first - 193) * 192 + (w.second - 64);
//        } else {
//            qDebug() << "encoding.cpp 223: 数据不在编码范围内";
//        }

//        //3.将结果转换为13位二进制
//        std::bitset<13> bits = val;
//        tmp_str = bits.to_string();
//        for(int j = 0; j < tmp_str.length(); ++j) {
//            encodingBits.push_back(tmp_str.at(j));
//        }
//    }

    return encodingBits;
}

std::vector<std::vector<char> > Encoding::BreakUpIntoCodeWords(std::vector<char> data, int capacity)
{
    std::vector<std::vector<char> > _CodeWords;
    if(int tmp = data.size() % 8) {
        for(int i = 0; i < (8-tmp); ++i) {
            data.push_back('0');
        }
    }
    //int numCodeWords = data.size() / 8;
    std::vector<char> tmp;
    for(int i = 0; i < data.size(); ++i){
        tmp.push_back(data.at(i));
        if(!((i+1) % 8)){
            _CodeWords.push_back(tmp);
            tmp.clear();
        }
    }
    //当数据位不足时，交替添加填充码字11101100和00010001
    std::vector<char> padBytes_1{'1', '1', '1', '0', '1', '1', '0', '0'};
    std::vector<char> padBytes_2{'0', '0', '0', '1', '0', '0', '0', '1'};
    bool fl = true;
    for(int i = data.size() / 8; i < capacity; ++i){
        if(fl){
            _CodeWords.push_back(padBytes_1);
            fl = false;
        } else {
            _CodeWords.push_back(padBytes_2);
            fl = true;
        }
    }

    return _CodeWords;
}


std::vector<std::vector<char>> Encoding::GetCodeWords()
{
    return codeWords;
}

std::vector<char> Encoding::GetBinaryData()
{
    return binaryData;
}

}
